package top.huxy.qqbot.framework;

import com.alibaba.fastjson.JSON;
import top.huxy.qqbot.acks.Ack;
import top.huxy.qqbot.acks.Login;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author HuXY
 */
public final class NodeQQSession implements ThreadFactory {

    private final String ip;
    private final int port;
    private final long qq;
    private final String pwd;
    private final String verification;
    private final EventHandler eventHandler;

    private Socket socket;

    private Runnable runnable;
    private final ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(0, 100, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), this);

    private static final AtomicInteger THREAD_ID = new AtomicInteger(1);

    public NodeQQSession(String ip, int port, long qq, String pwd, EventHandler eventHandler, String verification) {
        if (eventHandler == null) {
            throw new NullPointerException("eventHandler must be not null");
        }

        this.ip = ip;
        this.port = port;
        this.qq = qq;
        this.pwd = pwd;
        this.eventHandler = eventHandler;
        this.verification = verification;

        this.initSocket();
    }

    private void initSocket() {
        if (runnable != null) {
            throw new IllegalStateException("socket has been initialized");
        }

        runnable = () -> {
            createSocket();

            while (true) {
                InputStream inputStream = null;
                try {
                    inputStream = socket.getInputStream();
                } catch (IOException e) {
                    try {
                        eventHandler.onSocketGetInputStreamException(e, this);
                    } catch (Exception ee) {
                        ee.printStackTrace();
                    }
                    continue;
                }
                byte[] data;
                try {
                    byte[] buff = new byte[1024];
                    int read = inputStream.read(buff);
                    if (read < 0) {
                        throw new IOException("read is " + read);
                    } else {
                        data = Arrays.copyOfRange(buff, 0, read);
                    }
                } catch (IOException e) {
                    try {
                        eventHandler.onReadInputStreamException(e, this);
                    } catch (Exception ee) {
                        ee.printStackTrace();
                    }
                    continue;
                }

                //异步处理消息，提升性能
                poolExecutor.execute(() -> {
                    eventHandler.beforeHandlerMessage();
                    eventHandler.onMessage(data, this);
                    eventHandler.afterHandlerMessage();
                });
            }
        };

        poolExecutor.execute(runnable);
    }

    private void sendMessage(Ack ack) {
        if (ack != null) {
            byte[] bytes = JSON.toJSONBytes(ack);
            try {
                /*
                 * 在bytes后面添加一个\0,代表结束
                 */
                byte[] data = new byte[bytes.length + 1];
                System.arraycopy(bytes, 0, data, 0, bytes.length);
                data[data.length - 1] = '\0';

                eventHandler.beforeSendMessage(ack);
                OutputStream outputStream = socket.getOutputStream();
                outputStream.write(data);
                outputStream.flush();
                eventHandler.afterSendMessage(ack);
            } catch (IOException e) {
                try {
                    eventHandler.onSendMessageException(e, this, ack);
                } catch (Exception ee) {
                    ee.printStackTrace();
                }
            }
        }
    }

    public void sendMessage(List<Ack> acks) {
        if (acks != null && !acks.isEmpty()) {
            for (Ack ack : acks) {
                this.sendMessage(ack);
            }
        }
    }

    void createSocket() {
        try {
            eventHandler.beforeCreateSocket(ip, port);
            socket = new Socket(ip, port);
            eventHandler.afterCreateSocket(this);
            eventHandler.beforeQQLogin(qq, pwd);
            //进行登录
            sendMessage(new Login(qq, pwd, verification));
        } catch (Exception e) {
            try {
                eventHandler.onSocketInitException(e, this);
            } catch (Exception ee) {
                ee.printStackTrace();
            }
            try {
                Thread.sleep(10000);
            } catch (InterruptedException ignored) {
            }
            this.createSocket();
        }
    }

    @Override
    public Thread newThread(Runnable r) {
        return new Thread(r, "HuXY-QQ-Bot-" + THREAD_ID.getAndIncrement());
    }
}
